﻿using DimensionsHelper.Common.Utils;
using DimensionsHelper.LanguageService.Symbols;
using DimensionsHelper.LanguageService.Syntax;

namespace DimensionsHelper.LanguageService;

/// <summary>
///     语法分析树的基本节点，在<see cref="Parser" />进行语法分析后，由<see cref="Binder" />
///     进行语义绑定。此类节点在语法节点<see cref="SyntaxNode" />的基础上进行包装，添加一些新的内容，
///     例如，绑定节点的语法符号（类、接口、本地函数等）。
/// </summary>
/// <param name="syntaxNode">语法分析节点</param>
public abstract class AnalysisNode(AnalysisBoundKinds kind, SyntaxNode syntaxNode, AnalysisNode? parent)
{
    private WeakReference<ITypeSymbol>? _containingWithSymbol;

    private WeakReference<ISymbol>? _symbol;


    /// <summary>
    ///     符号绑定类型
    /// </summary>
    public AnalysisBoundKinds BoundKind { get; } = kind;


    /// <summary>
    ///     子节点数量
    /// </summary>
    public abstract int ChildrenCount { get; }


    /// <summary>
    ///     原始语法节点
    /// </summary>
    public virtual SyntaxNode Syntax { get; } = syntaxNode;


    /// <summary>
    ///     当前节点的父级节点
    /// </summary>
    public AnalysisNode? Parent { get; internal set; } = parent;


    /// <summary>
    ///     获取语法节点的文本区间
    /// </summary>
    public TextSpan Span => Syntax.Span;


    /// <summary>
    ///     获取节点文本内容
    /// </summary>
    public string Text => Syntax.Text;


    /// <summary>
    ///     如果节点是表达式，返回<see langword="true" />
    /// </summary>
    public abstract bool IsExpression { get; }


    /// <summary>
    ///     如果节点是语句，返回<see langword="true" />
    /// </summary>
    public abstract bool IsStatement { get; }


    /// <summary>
    ///     如果节点是元数据节点，返回<see langword="true" />
    /// </summary>
    public abstract bool IsMetadata { get; }


    /// <summary>
    ///     获取包含当前节点的<see cref="WithStatement" />的表达式符号。
    /// </summary>
    public ITypeSymbol? GetContainingWithSymbol()
    {
        return _containingWithSymbol?.TryGetTarget(out ITypeSymbol? symbol) == true ? symbol : null;
    }


    /// <summary>
    ///     设置当前节点所在的<see cref="WithStatement" />的表达式符号
    /// </summary>
    internal void SetContainingWithSymbol(ITypeSymbol symbol)
    {
        _containingWithSymbol = new WeakReference<ITypeSymbol>(symbol);
    }


    /// <summary>
    ///     获取当前节点的绑定符号，如果未绑定，返回<see cref="UndefinedSymbol.Default" />
    /// </summary>
    public ISymbol GetSymbol()
    {
        return _symbol?.TryGetTarget(out ISymbol? symbol) == true ? symbol : UndefinedSymbol.Default;
    }


    /// <summary>
    ///     获取当前节点绑定的类型符号，如果未绑定，返回<see cref="ValueTypeSymbol.Any" />。
    /// </summary>
    public ITypeSymbol GetTypeSymbol()
    {
        return _symbol?.TryGetTarget(out ISymbol? symbol) == true
            ? symbol as ITypeSymbol ?? ValueTypeSymbol.Any
            : ValueTypeSymbol.Any;
    }


    /// <summary>
    ///     绑定当前节点的语法符号。
    /// </summary>
    /// <param name="symbol"></param>
    internal void Bind(ISymbol symbol)
    {
        _symbol = new WeakReference<ISymbol>(symbol);
        symbol.Reference(Syntax.GetLocation());
    }


    /// <summary>
    ///     遍历每一个子节点。
    /// </summary>
    public void ForEachChild(Action<AnalysisNode> callback)
    {
        GetChildren().ForEach(callback);
    }


    /// <summary>
    ///     枚举子节点
    /// </summary>
    public abstract IEnumerable<AnalysisNode> GetChildren();


    /// <summary>
    ///     向子节点列表的末尾添加一个子节点
    /// </summary>
    /// <param name="child">待添加的子节点</param>
    internal abstract void AddChild(AnalysisNode child);


    /// <summary>
    ///     如果当前符合<see cref="AnalysisTreeVisitor" />的判定，返回当前节点。
    /// </summary>
    public virtual AnalysisNode? AcceptVisitor(AnalysisTreeVisitor visitor)
    {
        AnalysisNode? visitThis = visitor.Visit(this);

        if (visitThis == null)
        {
            return null;
        }

        foreach (AnalysisNode? child in GetChildren())
        {
            AnalysisNode? visitNode = visitor.Visit(child);
            if (visitNode != null)
            {
                return visitNode;
            }
        }

        return this;
    }
}


public static class AnalysisNodeExtensions
{
    public static T WithContainingWithSymbol<T>(this T node, ITypeSymbol? symbol) where T : AnalysisNode
    {
        if (symbol != null)
        {
            node.SetContainingWithSymbol(symbol);
        }

        return node;
    }
}